﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Road3App.Model.CrossroadNs;
using Road3App.Model.CrossroadNs.TrafficlightNs;
using Road3App.Model.RoadNs;

namespace Road3App.Model
{
    interface IRoadNetworkSerializer
    {
        void Save(string name, RoadNetwork rn);
        RoadNetwork Load(string name);
    }

    class XmlRoadNetworkSerializer : IRoadNetworkSerializer
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name">Path to XML file with network</param>
        /// <param name="rn"></param>
        public void Save(string name, RoadNetwork rn)
        {
            // vehicle generators
            XElement xvgs = new XElement("vgs", rn.VGCollection.Select(vg =>
                new XElement("vg",
                    new XElement("id", vg.ID),
                    new XElement("intensity", vg.Intensity),
                    new XElement("next", vg.NextContainer != null ? vg.NextContainer.ID : Guid.Empty),
                    new XElement("x", vg.X),
                    new XElement("y", vg.Y),
                    new XElement("angle", vg.Angle))));

            // roads
            XElement xroads = new XElement("roads", rn.RoadCollection.Select(road =>
                new XElement("road",
                    new XElement("id", road.ID),
                    new XElement("length", road.Length),
                    new XElement("lanes", road.Lanes),
                    new XElement("speedlimit", road.SpeedLimit),
                    new XElement("next", road.NextContainer != null ? road.NextContainer.ID : Guid.Empty),
                    new XElement("x", road.X),
                    new XElement("y", road.Y),
                    new XElement("angle", road.Angle))));

            // crossroads
            XElement xcrs = new XElement("crs", rn.CRCollection.Select(cr =>
                new XElement("cr",
                    new XElement("id", cr.ID),
                    new XElement("x", cr.X),
                    new XElement("y", cr.Y),
                    new XElement("angle", cr.Angle),
                    new XElement("grid",
                        new XElement("width", cr.width),
                        new XElement("height", cr.height),
                        new XElement("cells", cr.Grid.Select(c =>
                            new XElement("cell",
                                new XElement("id", c.ID),
                                new XElement("x", c.Coords.X),
                                new XElement("y", c.Coords.Y),
                                new XElement("state", c.State))))),
                    new XElement("phases", cr.PhaseCollection.Select(p =>
                        new XElement("phase",
                            new XElement("id", p.ID),
                            new XElement("index", p.Index),
                            new XElement("length", p.Length),
                            new XElement("states", p.States.Select(s =>
                                new XElement("state",
                                    new XAttribute("x", s.Key.X),
                                    new XAttribute("y", s.Key.Y),
                                    s.Value)))))),
                    new XElement("connections", cr.RCRConnector.Connections.Select(c =>
                        new XElement("connection",
                            new XElement("id", c.ID),
                            new XElement("cell", c.Cell.ID),
                            new XElement("road", c.Road.ID),
                            new XElement("lane", c.Lane)))))));

            XElement network = new XElement("roadnetwork", xvgs, xroads, xcrs);
            network.Save(name);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name">Path to XML file with network</param>
        /// <returns></returns>
        public RoadNetwork Load(string name)
        {
            Func<XElement, string, string> xv = (xe, s) => xe.Element(s).Value;

            RoadNetwork rn = new RoadNetwork();
            XElement network = XElement.Load(name);

            rn.VGCollection = network.Element("vgs").Elements("vg").Select(xvg =>
                new VehicleGenerator()
                {
                    ID = Guid.Parse(xvg.Element("id").Value),
                    Intensity = double.Parse(xvg.Element("intensity").Value, CultureInfo.InvariantCulture),
                    X = double.Parse(xvg.Element("x").Value),
                    Y = double.Parse(xvg.Element("y").Value),
                    Angle = int.Parse(xvg.Element("angle").Value),
                    NextContainer = new Road() { ID = Guid.Parse(xvg.Element("next").Value) } // stub
                }
                ).ToList();

            rn.RoadCollection = network.Element("roads").Elements("road").Select(xroad =>
                new Road()
                {
                    ID = Guid.Parse(xroad.Element("id").Value),
                    Length = int.Parse(xroad.Element("length").Value),
                    Lanes = int.Parse(xroad.Element("lanes").Value),
                    SpeedLimit = int.Parse(xroad.Element("speedlimit").Value),
                    X = double.Parse(xroad.Element("x").Value),
                    Y = double.Parse(xroad.Element("y").Value),
                    Angle = int.Parse(xroad.Element("angle").Value),
                    NextContainer = new Road() { ID = Guid.Parse(xroad.Element("next").Value) } // stub
                }
                ).ToList();

            rn.CRCollection = network.Element("crs").Elements("cr").Select(xcr =>
                {
                    CrossRoad cr = new CrossRoad();
                    cr.ID = Guid.Parse(xcr.Element("id").Value);
                    cr.X = double.Parse(xcr.Element("x").Value);
                    cr.Y = double.Parse(xcr.Element("y").Value);
                    cr.Angle = int.Parse(xcr.Element("angle").Value);
                    cr.width = int.Parse(xcr.Element("grid").Element("width").Value);
                    cr.height = int.Parse(xcr.Element("grid").Element("height").Value);

                    cr.Grid = new ObservableCollection<Cell>(xcr.Element("grid").Element("cells").Elements("cell").Select(xc =>
                        new Cell()
                        {
                            ID = Guid.Parse(xc.Element("id").Value),
                            Coords = new Point(
                                int.Parse(xc.Element("x").Value),
                                int.Parse(xc.Element("y").Value)),
                            State = (CellStates)Enum.Parse(typeof(CellStates), xc.Element("state").Value)
                        }));

                    cr.PhaseCollection = new ObservableCollection<Phase>(xcr.Element("phases").Elements("phase").Select(xp =>
                        new Phase()
                        {
                            ID = Guid.Parse(xp.Element("id").Value),
                            Index = int.Parse(xp.Element("index").Value),
                            Length = int.Parse(xp.Element("length").Value),
                            States = xp.Element("states").Elements("state").ToDictionary(
                                xs => new Point(
                                    int.Parse(xs.Attribute("x").Value),
                                    int.Parse(xs.Attribute("y").Value)),
                                xs => (TrafficLightStates)Enum.Parse(typeof(TrafficLightStates), xs.Value))
                        }));

                    cr.RCRConnector = new RCRConnector();
                    cr.RCRConnector.Connections = new ObservableCollection<RCRCI>(xcr.Element("connections").Elements("connection").Select(xc =>
                        new RCRCI(
                            rn.RoadCollection.Where(r => r.ID == Guid.Parse(xc.Element("road").Value)).FirstOrDefault(),
                            int.Parse(xc.Element("lane").Value),
                            cr.Grid.Where(c => c.ID == Guid.Parse(xc.Element("cell").Value)).FirstOrDefault()
                            ) { ID = Guid.Parse(xc.Element("id").Value) }));

                    return cr;
                }
                ).ToList();

            rn.CRCollection.ForEach(cr => { cr.CreateTrafficLights(); cr.SwitchPhase(); });
            var containers = rn.VGCollection.Union<Container>(rn.RoadCollection.Union<Container>(rn.CRCollection));
            rn.RoadCollection.ForEach(r => r.NextContainer = containers.Where(c => c.ID == r.NextContainer.ID).FirstOrDefault());
            rn.VGCollection.ForEach(vg => vg.NextContainer = containers.Where(c => c.ID == vg.NextContainer.ID).FirstOrDefault());

            return rn;
        }
    }
}
